﻿/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

#include "../include/Live2dModelManager.hpp"
#include <string>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <Rendering/CubismRenderer.hpp>
#include "../include/Live2dPal.hpp"
#include "../include/Live2dDefine.hpp"
#include "../include/Live2dDelegate.hpp"
#include "../include/live2dModel.hpp"
#include "../include/Live2dView.hpp"

using namespace Csm;
using namespace Live2dDefine;
using namespace std;

namespace {
    Live2dModelManager* s_instance = NULL;

    void FinishedMotion(ACubismMotion* self)
    {
        Live2dPal::PrintLog("Motion Finished: %x", self);
    }
}

Live2dModelManager* Live2dModelManager::GetInstance()
{
    if (s_instance == NULL)
    {
        s_instance = new Live2dModelManager();
    }

    return s_instance;
}

void Live2dModelManager::ReleaseInstance()
{
    if (s_instance != NULL)
    {
        delete s_instance;
    }

    s_instance = NULL;
}

Live2dModelManager::Live2dModelManager()
{
}

Live2dModelManager::~Live2dModelManager()
{
    ReleaseAllModel();
}

void Live2dModelManager::ReleaseAllModel()
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        delete _models[i];
    }

    _models.Clear();
}

Live2dModel* Live2dModelManager::GetModel(csmUint32 no) const
{
    if (no < _models.GetSize())
    {
        return _models[no];
    }

    return NULL;
}

void Live2dModelManager::OnMove(csmFloat32 x, csmFloat32 y) const
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->SetDragging(x, y);
    }
}

void Live2dModelManager::OnClick(Csm::csmInt32 key, Csm::csmBool key_up) const
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->OnMouseClick(key, key_up);  
    }
}

void Live2dModelManager::OnLeftHandDown(Csm::csmBool key_up) const
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->OnLeftHandDown(key_up);
    }
}

void Live2dModelManager::OnRightHandDown(Csm::csmBool key_up) const
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->OnRightHandDown(key_up);
    }
}

void Live2dModelManager::OnStickLeftDown(const Csm::csmBool key_up)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->OnStickLeftDown(key_up);
    }
}

void Live2dModelManager::OnStickRightDown(const Csm::csmBool key_up)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->OnStickRightDown(key_up);
    }
}

void Live2dModelManager::OnStickShowLeftHand(const Csm::csmBool key_up)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->OnStickShowLeftHand(key_up);
    }
}

void Live2dModelManager::OnStickShowRightHand(const Csm::csmBool key_up)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->OnStickShowRightHand(key_up);
    }
}

void Live2dModelManager::OnStickL(const Csm::csmFloat32 x, const Csm::csmFloat32 y)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->OnStickL(x,y);
    }
}

void Live2dModelManager::OnStickR(const Csm::csmFloat32 x, const Csm::csmFloat32 y)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->OnStickR(x,y);
    }
}

void Live2dModelManager::StartMotion(const Csm::csmChar* group, const Csm::csmInt32 id)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->StartMotion(group, id);
    }
}

void Live2dModelManager::SetExpression(const Csm::csmChar* expressionId)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->SetExpression(expressionId);
    }
}

void Live2dModelManager::SetExpressionById(const Csm::csmInt32 expressionId)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->SetExpressionByID(expressionId);
    }
}

void Live2dModelManager::SetSpeed(const Csm::csmFloat32 speed)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->SetSpeed(speed);
    }
}

void Live2dModelManager::SetRandomMotion(const Csm::csmBool _random, const Csm::csmBool _randomExpression)
{
    for (csmUint32 i = 0; i < _models.GetSize(); i++)
    {
        Live2dModel* model = GetModel(i);

        model->RandomMotion(_random);
        model->RandomExpression(_randomExpression);
    }
}

void Live2dModelManager::OnUpdate() const
{
    CubismMatrix44 projection;
    int width, height;
    glfwGetWindowSize(Live2dDelegate::GetInstance()->GetWindow(), &width, &height);
    projection.Scale(1.0f, static_cast<float>(width) / static_cast<float>(height));

    
    projection.MultiplyByMatrix(Live2dDelegate::GetInstance()->GetView()->GetViewMatrix());
    

    const CubismMatrix44  saveProjection = projection;
    csmUint32 modelCount = _models.GetSize();
    for (csmUint32 i = 0; i < modelCount; ++i)
    {
        Live2dModel* model = GetModel(i);
        projection = saveProjection;

        // モデル1体描画前コール
        Live2dDelegate::GetInstance()->GetView()->PreModelDraw(*model);

        model->Update();
        model->Draw(projection);///< 参照渡しなのでprojectionは変質する

        // モデル1体描画後コール
        Live2dDelegate::GetInstance()->GetView()->PostModelDraw(*model);
    }
}

bool Live2dModelManager::ChangeScene(const csmChar* dir, const csmChar* fileName)
{
    if (DebugLogEnable)
    {
        Live2dPal::PrintLog("loading model : %s", fileName);
    }
    string filePath = string(dir) + fileName;
    if (!IsFileExist(filePath.c_str())) {
        Live2dPal::PrintLog("model : %s not exist", fileName);
        return false;
    }

    ReleaseAllModel();
    _models.PushBack(new Live2dModel());
    _models[0]->LoadAssets(dir, fileName);

    /*
     * モデル半透明表示を行うサンプルを提示する。
     * ここでUSE_RENDER_TARGET、USE_MODEL_RENDER_TARGETが定義されている場合
     * 別のレンダリングターゲットにモデルを描画し、描画結果をテクスチャとして別のスプライトに張り付ける。
     */
    {
#if defined(USE_RENDER_TARGET)
        // LAppViewの持つターゲットに描画を行う場合、こちらを選択
        Live2dView::SelectTarget useRenderTarget = Live2dView::SelectTarget_ViewFrameBuffer;
#elif defined(USE_MODEL_RENDER_TARGET)
        // 各LAppModelの持つターゲットに描画を行う場合、こちらを選択
        LAppView::SelectTarget useRenderTarget = LAppView::SelectTarget_ModelFrameBuffer;
#else
        // デフォルトのメインフレームバッファへレンダリングする(通常)
        Live2dView::SelectTarget useRenderTarget = Live2dView::SelectTarget_None;
#endif

#if defined(USE_RENDER_TARGET) || defined(USE_MODEL_RENDER_TARGET)
        // モデル個別にαを付けるサンプルとして、もう1体モデルを作成し、少し位置をずらす
        _models.PushBack(new Live2dModel());
        _models[1]->LoadAssets(dir, fileName);
        _models[1]->GetModelMatrix()->TranslateX(0.2f);
#endif

        Live2dDelegate::GetInstance()->GetView()->SwitchRenderingTarget(useRenderTarget);

        // 別レンダリング先を選択した際の背景クリア色
        float clearColor[3] = { 1.0f, 1.0f, 1.0f };
        Live2dDelegate::GetInstance()->GetView()->SetRenderTargetClearColor(clearColor[0], clearColor[1], clearColor[2]);
    }
    return true;
}

void Live2dModelManager::ReleaseScene()
{
    if (DebugLogEnable)
    {
        Live2dPal::PrintLog("release model");
    }   
    ReleaseAllModel();
}

csmUint32 Live2dModelManager::GetModelNum() const
{
    return _models.GetSize();
}
